package sf.callback.controller;

import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.X509EncodedKeySpec;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.pingplusplus.model.Charge;
import com.pingplusplus.model.Event;
import com.pingplusplus.model.Webhooks;

import sf.core.common.OrderStatus;
import sf.core.entity.TfOrder;
import sf.core.entity.TuMember;
import sf.core.event.XTRedisPublisher;
import sf.core.mapper.TfOrderMapper;
import sf.core.mapper.TuMemberMapper;
import sf.core.service.PushService;
import sf.core.util.NumberUtil;

@Controller
public class PingxxController {

	@Autowired
	private TfOrderMapper orderMapper;

	@Autowired
	private TuMemberMapper memberMapper;

	@Resource
	PushService pushService;

	@Autowired
	private XTRedisPublisher xtRedisPublisher;

	Logger logger = LoggerFactory.getLogger(PingxxController.class);

	private static PublicKey pubKey;

	static {
		try {
			pubKey = getPubKey();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public PingxxController() throws Exception {
		// pubKey = getPubKey();
	}

	@RequestMapping(value = "/pingxx", consumes = "application/json", produces = "application/json; charset=utf-8")
	@ResponseBody
	public void callback(@RequestBody String json, HttpServletRequest request, HttpServletResponse response)
			throws Exception {

		logger.error(json);

		byte[] sigBytes = request.getHeader("x-pingplusplus-signature").getBytes();

		if (!verifyData(json.getBytes(), base64(sigBytes), pubKey)) {
			response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			logger.error("verify failed!!!");
			return;
		}

		Event event = Webhooks.eventParse(json);

		if ("charge.succeeded".equals(event.getType())) {

			Charge charge = (Charge) Webhooks.parseEvnet(event.toString());

			// we test payment for now
			response.setStatus(HttpServletResponse.SC_OK);
			logger.error("Success!!!");

			TfOrder query = new TfOrder();
			query.setSerialnumber(new Long(charge.getOrderNo()));
			TfOrder order = orderMapper.selectOne(query);

			// verify order detail
			if (null != order && OrderStatus.STATUS_PAID != order.getStatus()
					&& order.getCost() * NumberUtil.ONE_HUNDRED_INT == charge.getAmount().intValue()) {

				TuMember member = memberMapper.selectByPrimaryKey(order.getMemberid());
				member.setBeans(member.getBeans() + order.getBeans() * NumberUtil.ONE_HUNDRED_INT);
				memberMapper.updateByPrimaryKeySelective(member);

				order.setStatus(OrderStatus.STATUS_PAID);
				orderMapper.updateByPrimaryKeySelective(order);
				xtRedisPublisher.sendPaySuccessMessage(order.getMemberid(), order.getId());
			}

		} else if ("refund.succeeded".equals(event.getType())) {
			response.setStatus(HttpServletResponse.SC_OK);
		} else {
			response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		}
	}

	private byte[] base64(byte[] bytes) throws Exception {
		String pubKey = new String(bytes, "UTF-8");
		return Base64.decodeBase64(pubKey);
	}

	private static PublicKey getPubKey() throws Exception {

		byte[] keyBytes = IOUtils.toByteArray(PingxxController.class.getResourceAsStream("/pingpp.pub"));
		String pubKey = new String(keyBytes, "UTF-8");
		pubKey = pubKey.replaceAll("(-+BEGIN PUBLIC KEY-+\\r?\\n|-+END PUBLIC KEY-+\\r?\\n?)", "");
		keyBytes = Base64.decodeBase64(pubKey);
		// generate public key
		X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance("RSA");
		PublicKey publicKey = keyFactory.generatePublic(spec);
		return publicKey;
	}

	private boolean verifyData(byte[] data, byte[] sigBytes, PublicKey publicKey)
			throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
		Signature signature = Signature.getInstance("SHA256withRSA");
		signature.initVerify(publicKey);
		signature.update(data);
		return signature.verify(sigBytes);
	}
}
